
use std::sync::{mpsc, Arc, Barrier, Condvar, Mutex, Once, OnceLock};
use std::cell::{OnceCell, };
use std::thread::{self, Thread};
use crossbeam::{scope, atomic::AtomicCell, select, channel::unbounded};

// 
pub fn test_cb_select_channel() {
    let (tx1, rx1) = unbounded::<String>();
    let (tx2, rx2) = unbounded::<String>();
    const msg_cnt: usize = 10;
    scope(|s| {
        s.spawn(move |_| {
            for i in 0..msg_cnt {
                tx1.send("channel-1 : ".to_string() + &i.to_string()).unwrap();
            }
        });
        s.spawn(move |_| {
            for i in 0..msg_cnt {
                tx2.send("channel-2 : ".to_string() + &i.to_string()).unwrap();
            }
        });
    }).unwrap();

    let mut complete_flag1 = false;
    let mut complete_flag2 = false;
    while !complete_flag1 || !complete_flag2 {
        select! {
            recv(rx1) -> msg => {
                if complete_flag1 {
                    continue;
                }
                if msg.is_err() {
                    complete_flag1 = true;
                }
                println!("From rx1: {:?}", msg);
            },
            recv(rx2) -> msg => {
                if complete_flag2 {
                    continue;
                }
                if msg.is_err() {
                    complete_flag2 = true;
                }
                println!("From rx2: {:?}", msg);
            },
        }
    }
    println!("msg recv complete"); 
}

pub fn test_cb_atomiccell() {
    let value: AtomicCell<u32> = AtomicCell::new(0);
    crossbeam::scope(|s| {
        s.spawn(|_| { value.fetch_add(1); });
        s.spawn(|_| { value.fetch_sub(1); });
    }).unwrap();
    assert_eq!(value.load(), 0); // 线程安全操作
}

// 
static mut V: u32 = 0;
static INIT: Once = Once::new();   
fn get_cache() -> u32 {
    unsafe {
        INIT.call_once(|| {
            println!("once::call_once");
            V = 32;
        });
        V
    }
}
pub fn test_once() {
    let r = get_cache();
    println!("first get cache : {r}");
    let r = get_cache();
    println!("second get cache : {r}");
}

pub fn test_oncecell() {
    let initv = 32;
    let c: OnceCell<u32> = OnceCell::new();
    assert!(c.get().is_none());
    let v = c.get_or_init(move || {initv});
    assert!(*v == 32);
    let v2 = c.get_or_init(move || {unreachable!()});
    println!("oncecell : {v2}");
}

pub fn test_oncelock() {
    static once_lock: OnceLock<u32> = OnceLock::new();
    println!("main-thread, before init, once-lock : {:?}", once_lock.get());
    thread::spawn(|| {
        let v = once_lock.get_or_init(|| {32});
        println!("init-thread v : {v}");
    }).join().unwrap();

    let v = once_lock.get_or_init(|| {unreachable!()});
    println!("main-thread, after init, once-lock : {v}");
}

pub fn test_channel() {
    let (tx, rx) = mpsc::channel::<String>();
    for _ in 0..3 {
        let sender = tx.clone();
        let _ = thread::spawn(move || {
            let msg = thread::current().id().as_u64().to_string() + "_msg";
            sender.send(msg).unwrap();
        });
    }
    drop(tx);
    while let Ok(msg) = rx.recv() {
        println!("msg : {}", msg);
    }
}

pub fn test_barrir() {
    let cnt = 10;
    let mut hdls: Vec<thread::JoinHandle<()>> = Vec::<thread::JoinHandle<_>>::with_capacity(cnt);
    let barrier = Arc::new(Barrier::new(cnt));
    for _ in 0..cnt {
        let b_cloned = barrier.clone();
        hdls.push(thread::spawn(move || {
            println!("{:?} : before wait barrier", thread::current().id());
            let r = b_cloned.wait();
            println!("{:?} : after wait barrier, is_leader: {}", thread::current().id(), r.is_leader());
        }));
    }
    for hdl in hdls {
        hdl.join().unwrap();
    }
}

pub fn test_condvar() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair_cloned = pair.clone();
    let r = scope(|s| {
        s.spawn(move |_| {
            println!("before consume");
            let mut guard = pair_cloned.0.lock().unwrap();
            let cond = &pair_cloned.1;
            while !*guard {
                guard = cond.wait(guard).unwrap();
            }
            println!("after consume: {}", *guard);
        });
        s.spawn(move |_| {
            println!("before produce");
            let mut guard = pair.0.lock().unwrap();
            *guard = true;
            println!("after produce : {}", *guard);
            let cond = &pair.1;
            cond.notify_all();
        });       
    });
}

